// 阻塞队列的实现
#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>

const int defaultcap = 5; // 队列大小，缺省值

template <typename T>
class BlockQueue
{
private:
    // 判断阻塞队列是否满了
    bool IsFull() { return _q.size() >= _cap; }
    // 判断阻塞队列是不是空的
    bool IsEmpty() { return _q.empty(); }

public:
    BlockQueue(int cap = defaultcap)
        : _cap(cap), csleep_num(0), psleep_num(0)
    {
        // 初始化条件变量和锁
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_full_cond, nullptr);
        pthread_cond_init(&_empty_cond, nullptr);
    }

    void Equeue(const T &in)
    {
        // 访问了临界资源
        pthread_mutex_lock(&_lock);

        // 生产者调用
        if (IsFull())
        {
            // 阻塞队列满了,生产者线程等待,等待要给锁，让他释放
            // 条件变量一般和互斥锁一起用
            psleep_num++;
            pthread_cond_wait(&_full_cond, &_lock);
            psleep_num--;
            // 当被唤醒时，线程是在临界区，所以继续加锁
            // 但这个动作都有pthread_cond_wait完成
            // 如果申请锁失败，就会在锁上阻塞等待
        }
        // 没满
        _q.push(in);

        // 到这里队列就一定有数据了
        // 可以唤醒消费者了
        // 临时方案
        if (csleep_num > 0)
        {
            pthread_cond_signal(&_empty_cond);
            std::cout << "唤醒了一个消费者" << std::endl;
        };
        pthread_mutex_unlock(&_lock);
    }

    T Pop()
    {
        // 消费者调用
        pthread_mutex_lock(&_lock);
        // 判断队列是否为空
        if (IsEmpty())
        {
            // 队列是空的，所以只能在条件变量下等待
            csleep_num++;
            pthread_cond_wait(&_empty_cond, &_lock);
            csleep_num--;
        }
        // 不为空
        T data = _q.front();
        _q.pop();

        // 到这里队列就一定有空间了,唤醒休眠的生产者
        if (psleep_num > 0)
        {
            pthread_cond_signal(&_full_cond);
            std::cout<< "唤醒了一个生产者" << std::endl;

        }

        pthread_mutex_unlock(&_lock);
        return data; // 消费了，把数据拿走了
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_full_cond);
        pthread_cond_destroy(&_empty_cond);
    }

private:
    std::queue<T> _q; // 队列,临界资源！！！
    int _cap;         // 容量

    pthread_mutex_t _lock; // 锁
    // 生产者一个条件变量，消费者一个条件变量
    pthread_cond_t _full_cond; // 条件变量，满了就放条件变量下
    pthread_cond_t _empty_cond;

    int csleep_num; // 消费者休眠的个数
    int psleep_num; // 生产者休眠的个数
};